package icasue.tools.sqlhelper.core;

import icasue.tools.sqlhelper.core.AbstractSqlProviderFrame;
import icasue.tools.sqlhelper.core.FrameParser;
import icasue.tools.sqlhelper.core.DBCol;
import lombok.SneakyThrows;
import icasue.reflect.annotations.NotNull;

import icasue.reflect.handles.fields.FieldOF;
import icasue.reflect.handles.object.ObjectOF;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.*;

import static icasue.tools.sqlhelper.common.Const.*;

class Insert extends FrameParser{


    @SneakyThrows(Throwable.class)
    final static String _chooseInsertMulti(@NotNull List<? extends Object> instMulti, boolean chooseAble, AbstractSqlProviderFrame frame){
        // 插入列名序列.
        final StringBuilder columnsNameSeq = new StringBuilder();
        // 插入值序列.
        final List<StringBuilder> allValueSeq = new LinkedList<>();
        // Java属性与列有效映射.
        final Map<String, String> fName_column = $fName_To_Column.apply(frame);


        // 需要选择插入的列映射.
        final Map<String, DBCol> insertChooseMapping = new LinkedHashMap<>(frame.dbTab.mappingRoot);
        if(chooseAble){
            // 参考第一个存在的Insert实例 过滤需要选择插入的映射。
            Object oneInst = instMulti.get(0);
            frame.dbTab.mappingRoot.values().stream()
                    .filter(dbCol -> ObjectOF.isNull_.test(FieldOF.get_.apply(dbCol.field,oneInst)))
                    .map(nullCol -> nullCol.getColumn())
                    .forEach(nullCol -> insertChooseMapping.remove(nullCol));
        }

        // 构建获取插入列名序列. [`col1`,col2`]
        insertChooseMapping.keySet().stream()
                .forEach(column -> columnsNameSeq.append($STRICT).append(column).append($STRICT).append($DOT));

        if(columnsNameSeq.length() > 0){
            columnsNameSeq.replace(columnsNameSeq.length() - 1,columnsNameSeq.length(),$EMPTY);
        }else {
            throw new SQLException(frame.database().concat($SPOT).concat(frame.table())
                    .concat(" has no actually columns to insert."));
        }

        // 创建value序列. ['val1',null,'va2']
        for (Object oneInst : instMulti) {
            StringBuilder oneValueSeq = new StringBuilder();
            insertChooseMapping.values().stream()
                    .map(dbCol -> FieldOF.get_.apply(dbCol.field,oneInst))
                    .forEach(val -> {
                        if(val == null){
                            oneValueSeq.append(NULL).append($DOT);
                        }else {
                            oneValueSeq.append($SEC).append(_formatVal(val)).append($SEC).append($DOT);
                        }
                    });
            // replace last ',' to "".
            oneValueSeq.replace(oneValueSeq.length() - 1,oneValueSeq.length(),$EMPTY);
            allValueSeq.add(oneValueSeq);
        }

        // 构建SQL Builder.
        StringBuilder sqlBuilder = new StringBuilder()
                // "insert into "
                .append(INSERT).append($SPACE).append(INTO).append($SPACE)
                // "insert into `database`.`table` "
                .append(frame.database()).append($SPOT).append(frame.table()).append($SPACE)
                // "insert into `database`.`table` [(`col1`,`col2`)] "
                .append($BRACKETS_SMALL_LEFT).append(columnsNameSeq.toString()).append($BRACKETS_SMALL_RIGHT).append($SPACE)
                // "insert into `database`.`table` [(`col1`,`col2`)] values "
                .append(VALUES).append($SPACE);

        // "insert into `database`.`table` [(`col1`,`col2`,`col3`)] values ('va1',null,'col3'),('va1',null,'col3')"
        allValueSeq.forEach(oneValueSeq ->
                sqlBuilder.append($BRACKETS_SMALL_LEFT)
                        .append(oneValueSeq.toString())
                        .append($BRACKETS_SMALL_RIGHT)
                        .append($DOT)
        );
        sqlBuilder.replace(sqlBuilder.length() - 1, sqlBuilder.length(), $EMPTY);

        String SQL = sqlBuilder.toString();
        return SQL;
    }

    /**
     * 构建插入SQL段.
     * @param inst 被执行插入的数据库实例.
     * @param chooseAble 是否插入段是可选择的(true: 属性有值才构建该段，false: 构建所有属性的段无论有没有值，空值使用null)
     * @param frame
     * @return
     * @throws IllegalAccessException
     */
    @SneakyThrows(Throwable.class)
    final static String _chooseInsert(@NotNull Object inst, boolean chooseAble, AbstractSqlProviderFrame frame) {
        // 插入列名序列.
        final StringBuilder columnsNameSeq = new StringBuilder();
        // 插入值序列.
        final StringBuilder valueSeq = new StringBuilder();

        for (Map.Entry<String, DBCol> fieldMap : frame.dbTab.mappingRoot.entrySet()) {
            String dbField = fieldMap.getKey();
            Field javaField = fieldMap.getValue().field;
            Object val = javaField.get(inst);
            if(val != null){
                // `col1`,col2`,
                columnsNameSeq.append($STRICT).append(dbField).append($STRICT).append($DOT);
                // 'val2','val2',
                valueSeq.append($SEC).append(_formatVal(val)).append($SEC).append($DOT);
            }else {
                if(!chooseAble){
                    // `col1`,col2`,
                    columnsNameSeq.append($STRICT).append(dbField).append($STRICT).append($DOT);
                    // 'val1',null,'va2'
                    valueSeq.append(NULL).append($DOT);
                }
            }
        }
        if(columnsNameSeq.length() > 0){
            columnsNameSeq.replace(columnsNameSeq.length() - 1,columnsNameSeq.length(),$EMPTY);
            valueSeq.replace(valueSeq.length() - 1,valueSeq.length(),$EMPTY);
        }else {
            throw new SQLException(frame.database().concat($SPOT).concat(frame.table())
                    .concat(" has no actually columns to insert."));
        }
        String SQL = new StringBuilder()
                // "insert into "
                .append(INSERT).append($SPACE).append(INTO).append($SPACE)
                // "insert into `database`.`table` "
                .append(frame.database()).append($SPOT).append(frame.table()).append($SPACE)
                // "insert into `database`.`table` [(`col1`,`col2`)] "
                .append($BRACKETS_SMALL_LEFT).append(columnsNameSeq.toString()).append($BRACKETS_SMALL_RIGHT).append($SPACE)
                // "insert into `database`.`table` [(`col1`,`col2`)] values "
                .append(VALUES).append($SPACE)
                // "insert into `database`.`table` [(`col1`,`col2`)] values [('val1','val2')]"
                .append($BRACKETS_SMALL_LEFT).append(valueSeq.toString()).append($BRACKETS_SMALL_RIGHT)
                .toString();
        return SQL;
    }
}